#! /usr/bin/perl

#use the HMTLSubs package and ValidationSubs package as well as the database and the CGI package
use HTMLSubs;
use ValidationSubs;
use DBQueries;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use CAM::PDF;

#also enable strict mode
use strict;

#create new CGI object
my $CGIHandle = new CGI;

#declare username to hold owner of the file
my $username;

#if the only input is username i.e. the size is 1, 
if(scalar($CGIHandle->param) == 1){
	
	#therefore isolate the username and proceed
	$username = $CGIHandle->param('username');
	
	#now call the subroutine to display the group submission form
	display_file_submission("");
}
#otherwise we know a new file was submitted so act accordingly
else{
	
	#now call subroutine to process input
	#pass this sub the reference to submitted info
	#this subroutine will take control of the flow of the script and determine what actions to further take
	process_file_submission();
}

#subroutine to process the file submission once it happened
sub process_file_submission{
	
	#obtain filename and remove slashes
  	my $filename = $CGIHandle->param("submittedFile");
  	$filename =~ s/.*[\/\\](.*)/$1/;

	#remove spaces
	$filename =~s/ /_/g;
  	
  	#also obtain username of owner
  	$username = $CGIHandle->param("username");

	if(ValidationSubs->validate_file_name($username, $filename) == 1){
  	
		#now add the relevant information to the database 
  		DBQueries::addFile($username, $filename);
  		
  		#call sub to add to the log file
  		my $log_text = "$ENV{'REMOTE_ADDR'} uploaded $filename to the system.";
  		HTMLSubs -> update_log($log_text);

		#declare name for system
		my $system_name;

		#get the id and create the name for the system
		if($filename =~ m/txt$/){
			
			$system_name = DBQueries::getFileID($username, $filename) . "\.txt";
		}
		#otherwise its a pdf
		else{
			$system_name = DBQueries::getFileID($username, $filename) . "\.pdf";
		}

  		#now use the cgi object to upload the file
  		my $upload_filehandle = $CGIHandle->upload("submittedFile");
  	
  		#open filehandle
  		open UPLOADFILE, ">files/$system_name";

  		while ( <$upload_filehandle> ){
    			print UPLOADFILE;
  		}

  		close UPLOADFILE;
  		
  		#now call method to analyze the document
  		#pass it the file id as well as the name the file has on the system
  		#call different sub depending on if a txt or pdt
  		if($filename =~ m/txt$/){
  			analyze_txt_document(DBQueries::getFileID($username, $filename), $system_name);
  		}
  		#otherwise it is pdf
  		else{
  			analyze_pdf_document(DBQueries::getFileID($username, $filename), $system_name);
  		}
  		
  	

		#call method to show result
  		show_success($filename);
  		
	}
	else{
		my $error_string = "<font color=\"red\">Your file: $filename is either empty or already taken or not a .txt or .pdf please try again!</font>";
  		display_file_submission($error_string);
	}
}

#subroutine that is called whenever the user succesfully submits a file
#this subroutine will display a success message to the user before redirecting to the main window
#this sub is passed the name of the uploaded file
sub show_success{
	
	#obtain parameter
	my $file_name = $_[0];
	
	#call method to display html beginnign
	HTMLSubs->generate_html_beginning("Successfully Uploaded a File!");
	
	#call sub to print header
	HTMLSubs -> generate_display_head("Document Management System", "Uploaded File", $username);
	
	print <<END_HTML;
	
	<!--now display the message to the user-->
	<h4>$file_name was succesfully uploaded.</h4>
	<br>
	
END_HTML

	#create hash for button
	my %username_hash;
	$username_hash{'username'} = $username;
	
	#call subroutine to display button that returns the user to the profile
	HTMLSubs -> generate_html_button("profile.pl.cgi", "Go To profile", \%username_hash);

	#now call method to produce closing html tags
	HTMLSubs->generate_html_end();
}

#subroutine to display the page that allows for a new file to be submitted
sub display_file_submission{
	
	#obtain potential messages
	my $error_text = $_[0];
	
	#start by calling the subroutine from the module to generate the beginning html code
	HTMLSubs->generate_html_beginning("Upload File");
	
	#next call internal subroutine to generate the html code for the submission from
	#this form will be targeted back to the same script so pass the name as a parameter
	generate_submission_html($ENV{'SCRIPT_NAME'}, $error_text);
	
	#lastly call subroutine to print the closing html tags
	HTMLSubs->generate_html_end();
}

#subroutine to create the html file sumbission form
#this sub is passed the name of the target for the form
sub generate_submission_html{
	
	#obtain target
	my $form_target = $_[0];
	
	#obtain potential errors
	my $error_text = $_[1];
	#call sub to print header
	HTMLSubs -> generate_display_head("Document Management System", "Upload Files", $username);
	
	print <<END_HTML;
END_HTML

	#display potential error message
	#print any potential error messages passed to the subroutine and ensure that not too many <br> tags are printed
	#by determining if the length of the text is 0 or not
	if(length($error_text) != 0){
		print "$error_text<br>";
	}

	#print the remainder
	print <<END_HTML;
	<table>
			<form action="$form_target" enctype="multipart/form-data" method=POST>
			<tr>
				<!-- file-->
				<td>Your Textfile:</td>
				<td><input type="file" name="submittedFile"></td>
				
				<!-- now create a hidden field that resubmits the usernmae so that the script knows what user is being passed back-->
				<input type="hidden" name="username" value="$username">
			</tr>
			<tr>
				<!--submit button-->
				<td><input type="submit" value="Submit"></form>
END_HTML
			#create hash for button
			my %username_hash;
			$username_hash{'username'} = $username;
			#call subroutine to crate a cancel button
			#this subroutine takes the name of a script and the value of the button 
			HTMLSubs -> generate_html_button("profile.pl.cgi", "Cancel", \%username_hash);
			
			#print remainder
			print <<END_HTML			
			</td>
			</tr>
	</table>
END_HTML
}

#subroutine to analyze a document
#this is passed the file id and name the file has on the system so that it can be easily retreived
sub analyze_txt_document{
	
	#obtain parameters 
	my $file_id = $_[0];
	my $system_file_name = $_[1];
	
	#clear any previous info 
	DBQueries::removeInfoID($file_id);
	
	#first obtain the count of each word
	#in order to analyze this file, a hash such that each word is the key to its specific count in the file will be used
	#this means that for each occurence of a word, the hash value indexed by that word will be incremented by one. 
	#declare the hash
	my %word_count = ();

	#declare a list to temporarily hold words of each line
	my @text_line;

	#declare scalar to temporarily hold individual words
	my $word;
	
	#open the filehandle
	open( FH, "files/$system_file_name");
	
	#loop through entirety of the file and place words in hash
	while ( <FH> ){ 
	
		#split the line into a list of words from that line
		#this splits around all non-word, underscore, new-line, and digit characters
	
		@text_line = split (/\W+|_+|\d+|\n+/,$_);
	
		#now loop through list and make lower case 
		#then add to or increment the count in the hash for each word
		foreach(@text_line){
		
			#make lowercase
			$word = lc($_);
		
			#if word is already present, increment the counter
			if(exists $word_count{$word}){
				$word_count{$word}++;
			} # end if
			
			#otherwise create a new element with count 1
			else{
				$word_count{$word} = 1;
			} # end else
		
		}#end foreach 
	}#end while

	#now that the hash is created, close the file
	close( FH );
	
	#count the number of unique words
	my $unique_words = keys(%word_count);

	#this will calculate the average length of words with regard to the number of occurences of each word
	#to do this, this code loops through the hash adding the length of each individual word and multiplying it by the number of occurences
	#at the same time a running total of the number of all words will be kept so that the average can be calculated accurately
	my $total_character_count = 0;
	my $total_word_count = 0;

	#loop through hash
	while(my($individual_word, $occurence) = each(%word_count)){
	
		$total_character_count += ($occurence * length($individual_word));
		$total_word_count += $occurence;
	}
	
	#now the overall average length can be calculated
	my $average_word_length = ($total_character_count / $total_word_count);

	#now sort by occurence
	#using this list, the keys (words) can be accessed directly in the hash
	my @sorted_words = sort{$word_count{$b} <=> $word_count{$a}} keys %word_count;
	
	#we now have a sorted list of all words in teh file by numer of occurences
	#loop through this list and add the information to the database
	
	foreach my $current_sorted_word(@sorted_words){
		
		#add each word to the database
		DBQueries::addInfo( $file_id, "keyword", $current_sorted_word, $word_count{$current_sorted_word});
	}
	
	#at this point all keywords are added to the database
	#add the average word length
	DBQueries::addInfo( $file_id, "avgWordLength", $average_word_length);
	
	#add word count
	DBQueries::addInfo( $file_id, "wordCount", $total_word_count);
	
	#also, obtain the file size
	my $file_size = -s "files/$system_file_name";
	
	#add to database
	DBQueries::addInfo( $file_id, "fileSize", $file_size);
	
}

#subroutine to analzye a pdf document
sub analyze_pdf_document{
	
	my $file_id = $_[0];
	my $system_file_name = $_[1];
	
	#clear any previous info 
	DBQueries::removeInfoID($file_id);
	
	#first obtain the count of each word
	#in order to analyze this file, a hash such that each word is the key to its specific count in the file will be used
	#this means that for each occurence of a word, the hash value indexed by that word will be incremented by one. 
	#declare the hash
	my %word_count = ();

	#declare a list to temporarily hold words of each line
	my @text_line;

	#declare scalar to temporarily hold individual words
	my $word;
	
	my $pdf = CAM::PDF->new( "files/$system_file_name" ) || "Error: $CAM::PDF::errstr\n" ;
	
	my $pages = $pdf->numPages() ;
	
	foreach my $p ( $pdf->rangeToArray( 1, $pdf->numPages() ) )
	{
		my $str = $pdf->getPageText( $p ) ;
		if( defined $str )
		{
			CAM::PDF->asciify( \$str ) ;
			
			#now a string is stored for each page
			#split the page into a list of words from that page
			#this splits around all non-word, underscore, new-line, and digit characters
	
			@text_line = split (/\W+|_+|\d+|\n+/,$str);
	
			#now loop through list and make lower case 
			#then add to or increment the count in the hash for each word
			foreach(@text_line){
		
				#make lowercase
				$word = lc($_);
		
				#if word is already present, increment the counter
				if(exists $word_count{$word}){
					$word_count{$word}++;
				} # end if
			
				#otherwise create a new element with count 1
				else{
					$word_count{$word} = 1;
				} # end else
		
			}#end foreach 	
		}
	}
	
	#count the number of unique words
	my $unique_words = keys(%word_count);

	#this will calculate the average length of words with regard to the number of occurences of each word
	#to do this, this code loops through the hash adding the length of each individual word and multiplying it by the number of occurences
	#at the same time a running total of the number of all words will be kept so that the average can be calculated accurately
	my $total_character_count = 0;
	my $total_word_count = 0;

	#loop through hash
	while(my($individual_word, $occurence) = each(%word_count)){
	
		$total_character_count += ($occurence * length($individual_word));
		$total_word_count += $occurence;
	}
	
	#now the overall average length can be calculated
	my $average_word_length = ($total_character_count / $total_word_count);

	#now sort by occurence
	#using this list, the keys (words) can be accessed directly in the hash
	my @sorted_words = sort{$word_count{$b} <=> $word_count{$a}} keys %word_count;
	
	#we now have a sorted list of all words in teh file by numer of occurences
	#loop through this list and add the information to the database
	
	foreach my $current_sorted_word(@sorted_words){
		
		#add each word to the database
		DBQueries::addInfo( $file_id, "keyword", $current_sorted_word, $word_count{$current_sorted_word});
	}
	
	#at this point all keywords are added to the database
	#add the average word length
	DBQueries::addInfo( $file_id, "avgWordLength", $average_word_length);
	
	#add word count
	DBQueries::addInfo( $file_id, "wordCount", $total_word_count);
	
	#also, obtain the file size
	my $file_size = -s "files/$system_file_name";
	
	#add to database
	DBQueries::addInfo( $file_id, "fileSize", $file_size);
}

